/**
 * @Author:yq
 * @createTime:2019-6-2
 * @Email:892280082@qq.com
 * 
 * 一般使用方法
 * 使用open方法，配置好设备连接。
 * connectSuccess事件可获取连接成功的设备ID，存入本地缓存，下次打开小程序传入open方法
 * 可直连蓝牙设备。
 * 当change事件>=8时，其它页面调用search方法即可查询身份证
 * 
 * -----------API------------
 * 1.开启设备
 * @lastDev 上一次连接的设备，启动后默认链接该设备
 * @adaptDevs 可适配的设备列表，启动后默认链接该数据内的设备，数组书序决定优先级
 * @debug 是否开启debug模式，打印日志
 * @interval 查询间隔
 * open({<lastDev>:String,<adaptDevs>:[String],<debug>:Boolean?false,<interval>:Integer?3*1000})
 * 
 * 2.监听事件 
 * @key 事件名称
 * @fn 触发回调事件
 * on(key:String,fn:Function)
 * 
 * 3.连接设备
 * @deviceId 自定的设备ID
 * connect(deviceId:String)
 * 
 * 4.查询身份证信息 
 * @fn 回调函数返回信息
 * @attention 如果查询失败，会重新打开蓝牙适配器 连接上一次成功的设备，再次查询
 * search(fn:()=>{name,gender,address,nation...})
 * 
 * 5.获取当前设备
 * @attention 在connectSuccess事件之后调用
 * @attention 该方法不建议使用
 * getCurrentDev():Devices 
 * 
 * 6.关闭设备
 * @deviceId 关闭的设备ID 如果不传入，则关闭当前链接的设备
 * closeDev(<deviceId>:String,cb:Function)
 * 
 * 7.发现设备
 * @triger Boolean  是否触发事件
 * discovery({<triger>:Boolean?false})
 * 
 * 8.删除所有监听器
 * @attention 当子页面使用了监听器，在页面销毁时，请务必调用该方法清空监听器
 * 以防监听器回调事件中调用Page对象，导致页面无法正常销毁
 * removeAllListen();
 * 
 */

const yqthen = require('./yqthen.js')
const { searchCardIsOk, selectCardIsOk, stringToArrayBuffer, readCardIsOk} = require('./util');

//服务和特征值
const ServiceIdTmp = "0000ffe0-0000-1000-8000-00805f9b34fb"//安卓机型需要
//默认特征值，不要改动变量名
let characteristicId = "0000ffe1-0000-1000-8000-00805f9b34fb"

//蓝牙设备指令
const SearchIdcardCommond = 'aaaaaa96690003200122';//寻卡
const SelectIdcardCommond = 'aaaaaa96690003200221';//选卡
const ReadIdcardCommond = 'aaaaaa96690003301023';//读卡

//监听事件
const Monitors = [
  { k: 'closeBleSuccess', t: '关闭蓝牙成功',state:true },
  { k: 'openBleSuccess', t: '打开蓝牙适配器成功！', state: true },
  { k: 'openBleError', t: '打开蓝牙适配器失败！' },
  { k: 'searchingSuccess', t: '蓝牙查询设备成功事件', state: true},
  { k: 'foundDev', t: '查询的设备', state: true },
  { k: 'closeBleSearching', t: '关闭蓝牙查询功能', state: true },
  { k: 'stateChange', t: '适配器状态改变' },
  { k: 'connecting', t: '正在连接蓝牙设备', state: true },
  { k: 'connectSuccess', t: '蓝牙设备连接成功', state: true },
  { k: 'characteristicId', t: '获取蓝牙特征值', state: true },
  { k: 'notifySuccess', t: '启用notify通信成功', state: true },
  { k: 'notifyError', t: '启用通信notify失败' },
  { k: 'sendSearchIdcardCommondSuccess', t: '发送寻卡指令成功', state: true },
  { k: 'sendSearchIdcardCommondError', t: '发送寻卡指令失败' },
  { k: 'searchIdCardSuccess', t: '寻卡成功！' },
  { k: 'sendSelectIdcardCommondSuccess', t: '发送选卡指令成功', state: true },
  { k: 'sendSelectIdcardCommondError', t: '发送选卡指令失败' },
  { k: 'SelectIdCardSuccess', t: '选卡成功！' },
  { k: 'sendReadIdcardCommondSuccess', t: '发送读卡指令成功', state: true },
  { k: 'sendReadIdcardCommondError', t: '发送读卡指令失败' },
  { k: 'sendReadIdcard', t: '读取到身份证信息', state: true },
  { k:'closeDev',t:'关闭设备',state:true}
  //{ k: 'change', t: '事件改变回调值' },
]

//change事件 回调值是触发事件
const STATE_VALUE = Monitors.filter(v => v.state).map(v => v.k)

//蓝牙缓存对象
const bleCache= {
  //查询间隔时长
  interval:3*1000,
  //查询设备信息缓存
  devCache:[],
  //查询状态缓存
  searchState:{ searchIdcard:false,selecIdCard:false },
  //链接设备信息缓存
  connectInfo:{},
  //请求参数缓存
  paramCache:{},
  getCurrentDev:()=>{
    const { deviceId } = bleCache.connectInfo;
    if (deviceId){
      return bleCache.devCache.find(v => v.deviceId == deviceId)
    }
  }
}

//简单的事件监听器
const EventTrgiger = {
  state:-1,
  _container:{},
  //监听
  on:(key,fn,debug)=>{
    if(debug){
      if (!EventTrgiger._container[key])
        EventTrgiger._container[key] = fn;
      return;
    }
    EventTrgiger._container[key] = fn;
    if(key === 'change')
      fn(EventTrgiger.state)
  },
  //触发
  triger:(key,param)=>{
    //如果监听了该触发事件
    const orderValue = STATE_VALUE.indexOf(key)
    if (orderValue > -1)
      EventTrgiger.triger('change',orderValue)

    EventTrgiger._container[key] && EventTrgiger._container[key](param)
  },
  //注册
  regist: key => param => EventTrgiger.triger(key,param),
  //临时使用 runFn调用reback参数 可以将事件还原
  tmpUse: (key,runFn) => {
    const oldfn = EventTrgiger._container[key];
    EventTrgiger.on(key,()=> {
      EventTrgiger.on(key, oldfn);
      runFn();
    });
  }
}

/**
 * 像蓝牙设备发送指令
 * key:string 事件关键字
 * commond:string 指令数据
 * errcb:Function 错误回调函数
 */
const sendCommd = ({ key, commond,fail }) => {
  wx.writeBLECharacteristicValue({
    ...bleCache.connectInfo,
    value: stringToArrayBuffer(commond),
    success: EventTrgiger.regist(`${key}Success`),
    fail:res => {
      EventTrgiger.triger(`${key}Error`,res)
      fail && fail();
    }
  })
}

//缓存蓝牙发送的字节包
const bleMsg = {
  init:()=>{
    bleMsg.cardInfoArray = new Array()
    bleMsg.index = 0
  },
  appendData: (intArray,cb) => {
    let { cardInfoArray, index } = bleMsg;
    const { byteLength } = intArray;

    for (var i = 0; i < byteLength; i++) {
      cardInfoArray[index] = intArray[i]
      index++//二代证 1297 三代证2321
    }
    bleMsg.index = index;

    const { length } = cardInfoArray;

    if (length == 2321 || length == 1297){
      cb(readCardIsOk(cardInfoArray))
    }
  }
}


//链接蓝牙设备 
//param mac 蓝牙地址
const open = param => {

  const { 
    lastDev = null,
    adaptDevs = [],
    debug = false,
    interval=3*1000
  } = param;

  bleCache.interval = interval;

 if(debug){
   const flag = '[DEBUG]:'
   STATE_VALUE.forEach((k,i)=>{
     console.log(`${flag}change触发事件:${k},值:${i}`);
   })
   Monitors.forEach(({ k, t})=>{
     EventTrgiger.on(k,res => {
       console.log(`${flag}${t},触发事件:[${k}],${res?'返回值:'+JSON.stringify(res):''}`);
     },debug)
   })
 }

 //缓存请求数据，以便查询出错，进行重新请求
 bleCache.paramCache = { ...param};

 yqthen(next => {

   EventTrgiger.triger("closeBleSuccess")
   wx.closeBluetoothAdapter({complete:v => next(null)})

 }).then(next => {

    //打开蓝牙适配器
    wx.openBluetoothAdapter({
      success: res => {
        EventTrgiger.triger("openBleSuccess",res)
        next();
      },
      fail: EventTrgiger.regist("openBleError")
    })

  }).then(next => {

    wx.onBluetoothAdapterStateChange(EventTrgiger.regist('stateChange'))

    wx.onBluetoothDeviceFound(res => {
      const {devices} = res

      //缓存设备
      bleCache.devCache = devices;

      //匹配到上次的蓝牙设备，直接连接
      if(lastDev){
        const findDev = devices.find(v => v.deviceId == lastDev)
        if(findDev){
          return connect(lastDev);
        }
      }

      //匹配到可适配蓝牙设备，直接连接
      if (adaptDevs && adaptDevs.length > 0){
        const findDev = devices.find(v => adaptDevs.indexOf(v.deviceId) >-1 )
        if (findDev){
          return connect(findDev.deviceId);
        }
      }
      
      //否则触发查询到设备事件
      EventTrgiger.triger('foundDev', devices);

    })

    //监听设备数据
    wx.onBLECharacteristicValueChange(characteristic => {
      let buffer = characteristic.value
      let dataView = new DataView(buffer)
      var intArray = new Uint8Array(buffer)//将arraybuffer转换为array

      const { searchIdcard, selecIdCard } = bleCache.searchState;

      if (!searchIdcard && searchCardIsOk(intArray)) {

        bleCache.searchState.searchIdcard = true;
        EventTrgiger.triger('searchIdCardSuccess')

        sendCommd({
          key: 'sendSelectIdcardCommond', 
          commond: SelectIdcardCommond
        });

      } else if (!selecIdCard && selectCardIsOk(intArray)){

        bleCache.searchState.selecIdCard = true;
        EventTrgiger.triger('SelectIdCardSuccess')

        sendCommd({
          key: 'sendReadIdcardCommond',
          commond: ReadIdcardCommond
        });

      } else if (searchIdcard && selecIdCard){
        //读取的身份证数据包
        bleMsg.appendData(intArray,person => {
          EventTrgiger.triger("sendReadIdcard", JSON.stringify(person));
          bleCache.searchCb(person);
          bleCache.searchCb = null;
        });
      }

    })


    next();

  }).then(next => {

    discovery({triger:true});

  })

}

//连接指定蓝牙设备
const connect = deviceId => {

  yqthen(next => {
    //关闭蓝牙查询
    wx.stopBluetoothDevicesDiscovery({
      complete:res => {
        EventTrgiger.triger("closeBleSearching",res)
        next();
      }
    })
  }).then(next => {

    //关闭当前链接的设备
    closeDev({cb:res => next()})

  }).then(next => {

    //连接该设备
    wx.createBLEConnection({
      deviceId,
      success:res => {
        EventTrgiger.triger("connecting")

        wx.getBLEDeviceServices({
          deviceId,
          success:res => {

            bleCache.connectInfo.deviceId = deviceId;
            EventTrgiger.triger("connectSuccess", deviceId)


            //缓存上一次连接成功的设备ID
            bleCache.paramCache.lastDev = deviceId;

            wx.getSystemInfo({
              success: ({ platform })=>{
                const serviceId = platform == "android" ? ServiceIdTmp : res.services[res.services.length - 1].uuid;

                wx.getBLEDeviceCharacteristics({
                  deviceId,
                  serviceId,
                  success:res => {

                    characteristicId = res.characteristics[0].uuid;
                    EventTrgiger.triger("characteristicId",characteristicId);

                    bleCache.connectInfo = { deviceId, serviceId, characteristicId };

                    wx.notifyBLECharacteristicValueChanged({
                      state: true, // 启用 notify 功能
                      ...bleCache.connectInfo,
                      success: EventTrgiger.regist("notifySuccess"),
                      fail: EventTrgiger.regist("notifyError")
                    })

                  }

                })

              }
            })

          }
        })

      }
    })
  })
}



//查询身份证
const search = (cb) =>{
  //重置查询状态
  bleMsg.init();
  bleCache.searchCb = cb;
  bleCache.searchState={ searchIdcard: false, selecIdCard: false }
  sendCommd({ 
    key: 'sendSearchIdcardCommond', 
    commond: SearchIdcardCommond,
    fail:res => {
      //重新打开蓝牙适配，并且连接设备
      open(bleCache.paramCache)
      //当notify启动成功时，再次请求
      EventTrgiger.tmpUse('notifySuccess',() => {
        search(cb);
      })
    }
  });
}

//关闭设备
const closeDev = ({ deviceId,cb}) => {
  deviceId =  deviceId || bleCache.connectInfo.deviceId
  if (!deviceId)
    return cb()
  wx.closeBLEConnection({
    deviceId,
    complete:res => {
      EventTrgiger.triger("closeDev", deviceId)
      cb(res);
    }
  })
}

//发现设备
const discovery = ({triger=false}) =>{
  const {interval} = bleCache;
  wx.startBluetoothDevicesDiscovery({
    interval,
    success:res =>{
      triger && EventTrgiger.triger('searchingSuccess',res)
    },
    fail: res => {
      triger && EventTrgiger.triger('searchingError',res)
    }
  })
}

const removeAllListen = ()=>{
  EventTrgiger._container= {};
}

module.exports ={
  open,
  on: EventTrgiger.on,
  connect,
  search,
  getCurrentDev:bleCache.getCurrentDev,
  closeDev,
  discovery,
  removeAllListen
}